package com.abou.manager.common.impl.product;

import java.io.File;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import com.abou.connector.dao.IProductDao;
import com.abou.constant.product.EClotheType;
import com.abou.constant.product.EProductType;
import com.abou.constant.product.ESortOrder;
import com.abou.manager.common.IProductManager;
import com.abou.manager.exception.EManagerErrorCode;
import com.abou.manager.exception.ManagerException;
import com.abou.model.common.product.Product;
import com.abou.model.common.product.search.SearchProductsResult;
import com.abou.mvc.controller.ACommonController;

/**
 * @see IProductManager
 * @author vvvq5150
 */
@Transactional(propagation = Propagation.REQUIRED)
public class ProductManagerImpl implements IProductManager {
  
  @Autowired
  private IProductDao productDao;
  
  @Value("${tmp.dir.path}")
  private String tmpDirPath;
  private File tmpDir;
  @Value("${dest.dir.path}")
  private String destDirPath;
  private File destinationDir;
  
  /**
   * @see com.abou.manager.common.IProductManager#createProduct(com.abou.model.common
   *      .product.Product)
   */
  public Product createProduct(Product product, ServletContext context)
      throws Exception {

    validateAndUpdateImg(product, context);
    
    if (!product.isClothe()) {
      
      product.setCategory(null);
      product.setClotheType(null);
      product.setSizes(null);
      product.setColors(null);
    }
    
    // creating the product
    productDao.create(product);
    
    return product;
  }
  
  /**
   * Deletes the images on the disk related to the passed product
   * 
   * @param product
   * @return
   */
  private boolean deleteImages(Product product) {

    boolean deleted = true;
    
    // deleting the image file 1
    String imagePath = product.getImageRealPath1();
    File imageFile = new File(imagePath);
    if (imageFile.exists()) {
      
      deleted = deleted && imageFile.delete();
    } else {
      
      deleted = true;
    }
    
    // deleting the image file 2
    if (!org.apache.commons.lang.StringUtils.isBlank(product.getImageRealPath2())) {
      imagePath = product.getImageRealPath2();
      imageFile = new File(imagePath);
      
      if (imageFile.exists()) {
        
        deleted = deleted && imageFile.delete();
        
      } else {
        
        deleted = true;
      }
    }
    
    // deleting the image file 3
    if (!org.apache.commons.lang.StringUtils.isBlank(product.getImageRealPath3())) {
      imagePath = product.getImageRealPath3();
      imageFile = new File(imagePath);
      
      if (imageFile.exists()) {
        
        deleted = deleted && imageFile.delete();
      } else {
        
        deleted = true;
      }
    }
    
    // deleting the image file 4
    if (!org.apache.commons.lang.StringUtils.isBlank(product.getImageRealPath4())) {
      imagePath = product.getImageRealPath4();
      imageFile = new File(imagePath);
      
      if (imageFile.exists()) {
        
        deleted = deleted && imageFile.delete();
      } else {
        
        deleted = true;
      }
    }
    
    return deleted;
  }
  
  /**
   * @throws Exception When the images file could not been deleted.
   * @see com.abou.manager.common.IProductManager#deleteProduct(int)
   */
  public void deleteProduct(int id) throws Exception {

    // getting the product
    Product product = productDao.find(id);
    
    // deleting the images files
    boolean deleted = deleteImages(product);
    
    // deleting the product
    if (deleted) {
      
      productDao.delete(product);
      
    } else {
      
      throw new Exception("Could not delete product with ID " + id);
    }
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#getProduct(int)
   */
  public Product getProduct(int id) {

    return productDao.find(id);
  }
  
  /**
   * @throws Exception
   * @see com.abou.manager.common.IProductManager#updateProduct(com.abou.model.common.product.Product)
   */
  public Product updateProduct(Product product, ServletContext context)
      throws Exception {

    validateAndUpdateImg(product, context);
    
    Product productInDb = productDao.find(product.getId());
    
    productInDb.setPrice(product.getPrice());
    productInDb.setName(product.getName());
    productInDb.setDescription(product.getDescription());
    
    productInDb.setImagePath1(product.getImagePath1());
    productInDb.setImagePath2(product.getImagePath2());
    productInDb.setImagePath3(product.getImagePath3());
    productInDb.setImagePath4(product.getImagePath4());
    productInDb.setImageRealPath1(product.getImageRealPath1());
    productInDb.setImageRealPath2(product.getImageRealPath2());
    productInDb.setImageRealPath3(product.getImageRealPath3());
    productInDb.setImageRealPath4(product.getImageRealPath4());
    
    productInDb.setClotheType(product.getClotheType());
    productInDb.setInPromo(product.getInPromo() == null ? false
        : product.getInPromo());
    productInDb.setInStock(product.getInStock());
    productInDb.setNewness(product.getNewness());
    productInDb.setProductSale(product.getProductSale());
    
    if (!product.isClothe()) {
      
      productInDb.setCategory(null);
      productInDb.setClotheType(null);
      productInDb.setSizes(null);
      productInDb.setColors(null);
      
    } else {
      
      productInDb.setCategory(product.getCategory());
      productInDb.setClotheType(product.getClotheType());
      productInDb.setSizes(product.getSizes());
      productInDb.setColors(product.getColors());
    }
    
    productDao.merge(productInDb);
    
    return product;
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#listProducts(java.lang.String,
   *      java.lang.String, com.abou.constant.product.EProductType,
   *      com.abou.constant.product.ESortOrder, int, int)
   */
  public SearchProductsResult listProducts(String name, String description,
      EProductType type, ESortOrder sortOrder, int pageIndex, int pageSize) {

    List<Product> products =
        productDao.findProducts(name, description, type, sortOrder, pageIndex,
            pageSize);
    
    return new SearchProductsResult(productDao.findProducts(name, description,
        type), products);
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#findProductByDescription(java.lang.String,
   *      com.abou.constant.product.ESortOrder, int, int)
   */
  public SearchProductsResult findProductByDescription(String description,
      ESortOrder sortOrder, int pageIndex, int pageSize) {

    List<Product> products =
        productDao.findProductByDescription(description, sortOrder, pageIndex,
            pageSize);
    
    return new SearchProductsResult(
        productDao.findProductByDescription(description), products);
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#findProductByName(java.lang.String,
   *      com.abou.constant.product.ESortOrder, int, int)
   */
  public SearchProductsResult findProductByName(String name,
      ESortOrder sortOrder, int pageIndex, int pageSize) {

    List<Product> products =
        productDao.findProductByName(name, sortOrder, pageIndex, pageSize);
    
    return new SearchProductsResult(
        productDao.findProductByName(name), products);
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#findProductByType(com.abou.constant.product.EProductType,
   *      com.abou.constant.product.ESortOrder, int, int)
   */
  public SearchProductsResult findProductByType(EProductType type,
      ESortOrder sortOrder, int pageIndex, int pageSize) {

    List<Product> products =
        productDao.findProductByType(type, sortOrder, pageIndex, pageSize);
    
    return new SearchProductsResult(
        productDao.findProductByType(type), products);
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#listAllProducts(com.abou.constant.product.ESortOrder,
   *      int, int)
   */
  public SearchProductsResult listAllProducts(ESortOrder sortOrder,
      int pageIndex, int pageSize) {

    List<Product> products =
        productDao.findAllProducts(sortOrder, pageIndex, pageSize);
    
    return new SearchProductsResult(productDao.findAllProducts(), products);
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#listProductsByNameDescription(java.lang.String,
   *      java.lang.String, com.abou.constant.product.ESortOrder, int, int)
   */
  public SearchProductsResult listProductsByNameDescription(String name,
      String description, ESortOrder sortOrder, int pageIndex, int pageSize) {

    List<Product> products =
        productDao.findProductsByNameDescription(name, description, sortOrder,
            pageIndex, pageSize);
    
    return new SearchProductsResult(productDao.findProductsByNameDescription(
        name, description), products);
  }
  
  /**
   * @see com.abou.manager.common.IProductManager#listProductsClothes(java.lang.String,
   *      java.lang.String, EProductType ,
   *      com.abou.constant.product.EClotheType,
   *      com.abou.constant.product.ESortOrder, int, int)
   */
  public SearchProductsResult listProductsClothes(String name,
      String description, EProductType productType, EClotheType clotheType,
      ESortOrder sortOrder, int pageIndex, int pageSize) {

    List<Product> products = null;
    SearchProductsResult results = null;
    
    if (clotheType == null) {
      
      products =
          productDao.findProductsClothesNoClotheType(name, description,
              productType, sortOrder, pageIndex, pageSize);
      results =
          new SearchProductsResult(productDao.findProductsClothesNoClotheType(
              name, description, productType), products);
    } else {
      
      products =
          productDao.findProductsClothes(name, description, productType,
              clotheType, sortOrder, pageIndex, pageSize);
      results =
          new SearchProductsResult(productDao.findProductsClothes(name,
              description, productType, clotheType), products);
    }
    
    return results;
  }
  
  /**
   * @param image
   * @return
   * @throws ServletException
   */
  private File createImageFile(String fileName, ServletContext servletContext)
      throws ServletException {

    tmpDir = new File(tmpDirPath);
    if (!tmpDir.isDirectory()) {
      throw new ManagerException(EManagerErrorCode.TEMPORARY_DIR_NOT_DIR);
    }
    
    // getting the real path
    String realPath = servletContext.getRealPath(destDirPath);
    destinationDir = new File(realPath);
    
    if (!destinationDir.isDirectory()) {
      throw new ManagerException(EManagerErrorCode.DESTINATION_DIR_NOT_DIR);
    }
    
    DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
    /*
     * Set the size threshold, above which content will be stored on disk.
     */
    fileItemFactory.setSizeThreshold(5 * 1024 * 1024); // 1 MB
    /*
     * Set the temporary directory to store the uploaded files of size above
     * threshold.
     */
    fileItemFactory.setRepository(tmpDir);
    
    return new File(destinationDir, fileName);
  }
  
  /**
   * @param product
   * @param result
   * @throws Exception
   */
  private void validateAndUpdateImg(Product product,
      ServletContext servletContext) throws Exception {

    CommonsMultipartFile image = product.getImage1();
    String fileName = null;
    File file = null;
    
    if (image != null && !StringUtils.isBlank(image.getOriginalFilename())) {
      
      // creating the image file
      fileName = image.getOriginalFilename();
      file = createImageFile(fileName, servletContext);
      
      if (file.exists()) {
        
        throw new ManagerException(
            EManagerErrorCode.FILE_ALREADY_EXISTS, ACommonController.IMAGE_ATT1);
        
      } else {
        
        // writing the file into the disk
        image.getFileItem().write(file);
        
        // setting the image path and creating the product
        product.setImagePath1(ACommonController.IMG_PRODUCT_FOLDER
            + file.getName());
        product.setImageRealPath1(file.getPath());
      }
    }
    
    image = product.getImage2();
    
    if (image != null && !StringUtils.isBlank(image.getOriginalFilename())) {
      
      // creating the image file
      fileName = image.getOriginalFilename();
      file = createImageFile(fileName, servletContext);
      
      if (file.exists()) {
        
        throw new ManagerException(
            EManagerErrorCode.FILE_ALREADY_EXISTS, ACommonController.IMAGE_ATT2);
        
      } else {
        
        // writing the file into the disk
        image.getFileItem().write(file);
        
        // setting the image path and creating the product
        product.setImagePath2(ACommonController.IMG_PRODUCT_FOLDER
            + file.getName());
        product.setImageRealPath2(file.getPath());
      }
    }
    
    image = product.getImage3();
    
    if (image != null && !StringUtils.isBlank(image.getOriginalFilename())) {
      
      // creating the image file
      fileName = image.getOriginalFilename();
      file = createImageFile(fileName, servletContext);
      
      if (file.exists()) {
        
        throw new ManagerException(
            EManagerErrorCode.FILE_ALREADY_EXISTS, ACommonController.IMAGE_ATT3);
        
      } else {
        
        // writing the file into the disk
        image.getFileItem().write(file);
        
        // setting the image path and creating the product
        product.setImagePath3(ACommonController.IMG_PRODUCT_FOLDER
            + file.getName());
        product.setImageRealPath3(file.getPath());
      }
    }
    
    image = product.getImage4();
    
    if (image != null && !StringUtils.isBlank(image.getOriginalFilename())) {
      
      // creating the image file
      fileName = image.getOriginalFilename();
      file = createImageFile(fileName, servletContext);
      
      if (file.exists()) {
        
        throw new ManagerException(
            EManagerErrorCode.FILE_ALREADY_EXISTS, ACommonController.IMAGE_ATT4);
        
      } else {
        
        // writing the file into the disk
        image.getFileItem().write(file);
        
        // setting the image path and creating the product
        product.setImagePath4(ACommonController.IMG_PRODUCT_FOLDER
            + file.getName());
        product.setImageRealPath4(file.getPath());
      }
    }
  }
}
